%% Author: wooray
%% Created: 2009-3-17
%% Description: TODO: Add description to airing_app
-module(airing_app).

-behaviour(application).
-behaviour(supervisor).

%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("ring.hrl").

%% --------------------------------------------------------------------
%% Behavioural exports
%% --------------------------------------------------------------------
%% application behaviour callback
-export([
     start/2,
     stop/1
        ]).

%% supervisor behaviour callback
-export([init/1]).

-export([start/0]).

%% --------------------------------------------------------------------
%% Internal exports
%% --------------------------------------------------------------------

%% --------------------------------------------------------------------
%% Macros
%% --------------------------------------------------------------------
-define(TOP_SUPERVISOR, ?MODULE).

-define(MAX_NUMBER_OF_WEBTOOLS, 256).
-define(DEFAULT_PORT, 8899).% must be >1024 or the user must be root on unix

%% --------------------------------------------------------------------
%% Records
%% --------------------------------------------------------------------

%% --------------------------------------------------------------------
%% API Functions
%% --------------------------------------------------------------------


%% ====================================================================!
%% External functions
%% ====================================================================!
start() ->
    application:start(airing).

%% --------------------------------------------------------------------
%% Func: start/2
%% Returns: {ok, Pid}        |
%%          {ok, Pid, State} |
%%          {error, Reason}
%% --------------------------------------------------------------------
start(_Type, _StartArgs) ->
    ensure_apps(),
    
    Config = get_config(),
    
    AppName = proplists:get_value(config_name, Config),
    app_util:init_default_logger(AppName),
    
    NodeName = proplists:get_value(node_name, Config),
    net_kernel:start([NodeName, longnames]),
    
    RingLord = proplists:get_value(ringlord_name, Config, 'ringlord@10.0.0.253'),
    true = net_kernel:connect_node(RingLord),
    global:sync(),
    
    log4erl:debug("APP Start Now: ~p", [NodeName]),

    case supervisor:start_link({local, airing_supervisor}, airing_app, [Config]) of
	{ok, Pid} ->
	    {ok, Pid};
	Error ->
	    Error
    end.

%% --------------------------------------------------------------------
%% Func: stop/1
%% Returns: any
%% --------------------------------------------------------------------
stop(_State) ->
    ok.

init([Config]) ->
    log4erl:debug("SUPERVISOR START: ~p", [Config]),
    Stragegy = {one_for_one, 10, 10},

    ConfigServer = {
        ai_config,
        {ai_config, start_link, []},
        permanent, brutal_kill, worker, [ai_config]
    },

    RingNode = new_ring_node(Config),
    NodeStore = {
        node_store, 
        {node_store, start_link, [RingNode]},
        transient, 1000, worker, [node_store]
    },
                
    {ok, {Stragegy, [
            ConfigServer,
            NodeStore
        ]}
    }.

%% ====================================================================
%% Internal functions
%% ====================================================================
ensure_apps() ->
    app_util:ensure_base_apps(),

    inets:start(),
    ok.

get_config() ->
    {ok, [[RingLordStr]]} = init:get_argument(ringlord),
    RingLord = list_to_atom("ringlord@" ++ RingLordStr),
    {ok, [[ConfigFilename]]} = init:get_argument(config),
    LocalIp = app_util:get_local_ip(),
    NodeName = list_to_atom(ConfigFilename ++ "@" ++ app_util:get_host_addr_str(LocalIp)),
    {ok, NodeId} = application:get_env(airing, node_id),
    [
     {config_name, ConfigFilename}, 
     {node_id, NodeId},
     {node_name, NodeName},
     {node_ip, LocalIp},
     {ringlord_name, RingLord}
    ].

new_ring_node(ConfigList) ->
    case get_free_port(?DEFAULT_PORT, ?MAX_NUMBER_OF_WEBTOOLS) of
    {error,Reason} -> throw({error,Reason});
    Port ->
        #ring_node{
            id          = proplists:get_value(node_id, ConfigList),
            name        = proplists:get_value(node_name, ConfigList),
            ip          = proplists:get_value(node_ip, ConfigList),
            access_port = Port
        }
    end.

get_free_port(_Port,0) ->
    {error,no_free_port_found};
get_free_port(Port,N) ->
    case gen_tcp:connect("localhost",Port,[]) of
    {error, _Reason} ->
        Port;
    {ok,Sock} ->
        gen_tcp:close(Sock),
        get_free_port(Port+1,N-1)
    end.

